/*****************************************************************************
 * 
 * Øving 2 UCSysDes
 *
 *****************************************************************************/

#include "oeving2.h"

/* lager peker til I/O-registrene til PIO port B */
volatile avr32_pio_t *piob = &AVR32_PIOB;
volatile avr32_pio_t *pioc = &AVR32_PIOC;
volatile avr32_dac_t *dac = &AVR32_DAC;
volatile avr32_sm_t *sm = &AVR32_SM;

int light = 1;// light position

Channel musicChannel = {0, 0, 0, 0};
Channel *musicCh_ptr = &musicChannel;
Channel sfxChannel1 = {0, 0, 0, 0};
Channel *sfxCh1_ptr = &sfxChannel1;


Song deathSong = {13, 13, 0, 0, 0, 0,
   
   //right
   {{1500, 4, 5, 0.2f},
    {1500, 3, 5, 0.5f},
    {1500, 2, 5, 1.0f},
    {1500, 1, 5, 1.5f},

    {1500, 4, 4, 1.5f},
    {1500, 3, 4, 1.0f},
    {1500, 2, 4, 0.5f},
    {1500, 1, 4, 0.2f},

    {1500, 4, 3, 0.2f},
    {1500, 3, 3, 0.5f},
    {1500, 2, 3, 1.0f},
    {1500, 1, 3, 1.5f},
    
    {10000, 4, 2, 1.5f}},
   //left
   {{1500, 4, 5, 1.5f},
    {1500, 3, 5, 1.0f},
    {1500, 2, 5, 0.5f},
    {1500, 1, 5, 0.2f},

    {1500, 4, 4, 0.2f},
    {1500, 3, 4, 0.5f},
    {1500, 2, 4, 1.0f},
    {1500, 1, 4, 1.5f},

    {1500, 4, 3, 1.5f},
    {1500, 3, 3, 1.0f},
    {1500, 2, 3, 0.5f},
    {1500, 1, 3, 0.2f},
    
    {10000, 4, 2, 1.5f}}
};
Song *deathSong_ptr = &deathSong;

Song wallHitSong = {2, 2, 0, 0, 0, 0,
    
    {{1500, 0, 5, 0.1f},
     {1500, 2, 6, 1.5f}},

    {{1500, 0, 5, 1.5f},
     {1500, 2, 6, 0.1f}}                    
};
Song *wallHitSong_ptr = &wallHitSong;

Song paddleHitSong = {4, 4, 0, 0, 0, 0,
    
    {{500, 0, 3, 1.5f},
     {500, 4, 6, 1.5f},
     {500, 0, 3, 1.5f},
     {500, 4, 6, 1.5f}},

    {{500, 0, 3, 1.5f},
     {500, 4, 6, 1.5f},
     {500, 0, 3, 1.5f},
     {500, 4, 6, 1.5f}}                    
};
Song *paddleHitSong_ptr = &paddleHitSong;


/* main kalles når programmet starter */
int main (int argc, char *argv[]) {
  initHardware();
  set_interrupts_base((void*) AVR32_INTC_ADDRESS);
  register_interrupt((__int_handler)(button_int),
                     AVR32_PIOB_IRQ / 32, AVR32_PIOB_IRQ % 32,
                     INT0);
  register_interrupt((__int_handler)(dac_int),
                     AVR32_DAC_IRQ / 32, AVR32_DAC_IRQ % 32,
                     INT0);
  init_interrupts();
  //infinite loop
  while(1){}
  return 0;
}

/* funksjon for å initialisere maskinvaren, må utvides */
void initHardware (void) {
  pioc->per = 0xff;  /*port c = leds, port b = buttons */
  pioc->oer = 0xff;
  piob->per = 0xff;
  piob->puer = 0xff;

  //also the uper 3 buttons are activated
  piob->ier = 0xe5;
  piob->idr = ~(0xe5);
  
  pioc->sodr = light;
  
  //setup of PIO for ABDAC
  piob->PDR.p20 = 1; // set bit 20 and 21
  piob->PDR.p21 = 1;
  piob->ASR.p20 = 1;
  piob->ASR.p21 = 1;
}

/*Sample a sine wave*/
short playSine(short note, short octave, float volume, Channel *channel, short right)
{
  short x = 0;
  if (note == -1)
  {
    return x;
  }

  if(right)
  {
    if(channel->right > toneSampleLength[note])
    {
      channel->right = 0;
    }

    if(octave > 4)
    {
      channel->right += (powerOfTwo[octave - 4]);
    }
    else if(octave < 4)
    {
      if(channel->rightRest >= powerOfTwo[4 - octave])
      {
        channel->rightRest = 0;
        channel->right++;
      }
      channel->rightRest++;
    }
    else
    {
      channel->right++;
    }
    x = (sines[note][channel->right] * volume);
  }
  else
  {
    if(channel->left > toneSampleLength[note])
    {
      channel->left = 0;
    }

    if(octave > 4)
    {
      channel->left += (powerOfTwo[octave - 4]);
    }
    else if(octave < 4)
    {
      if(channel->leftRest >= powerOfTwo[4 - octave])
      {
        channel->leftRest = 0;
        channel->left++;
      }
      channel->leftRest++;
    }
    else
    {
      channel->left++;
    }
    x = (sines[note][channel->left] * volume);
  }
  
  return x;
}

void loadSong(Channel *channel, Song *song)
{
  if(!channel->isActiveRight && !channel->isActiveLeft)
  {
    channel->song_ptr = song;
    channel->isActiveRight = 1;
    channel->isActiveLeft = 1;
  }
}

/*Interface method for sound playback*/
short play(short isRight)
{
  short total = 0;
  //Contribution from music channel
  if (musicCh_ptr->isActiveRight && isRight)
  {
    total += playSong(musicCh_ptr, 1);
  }
  else if(musicCh_ptr->isActiveLeft && !isRight)
  {
    total += playSong(musicCh_ptr, 0);
  }
  //Contribution from sfx channel 1
  if (sfxCh1_ptr->isActiveRight && isRight)
  {
    if(total != 0)
    {
      short sample2 = playSong(sfxCh1_ptr, 1);
      int combo = (total + sample2) / 2;
      total  = combo;
    }
    else
    {
      total += playSong(sfxCh1_ptr, 1);
    }
  }
  else if(sfxCh1_ptr->isActiveLeft && !isRight)
  {
    if(total != 0)
    {
      short sample2 = playSong(sfxCh1_ptr, 0);
      int combo = (total + sample2) / 2;
      total  = combo;
    }
    else
    {
      total += playSong(sfxCh1_ptr, 0);
    }
  }
  return total;

}

/*Controls the timing of a song and interfaces with playSine*/
short playSong(Channel *channel, short isRight)
{
  Song *song = channel->song_ptr;
  if(isRight)
  {
    song->noteTimerRight++;
    //if note is finished, progress to next note
    if(song->noteTimerRight >= song->notesRight[song->currentNoteRight].length)
    {
      song->currentNoteRight++;
      song->noteTimerRight = 0;
    }
    //if no more notes, reset the timing, set the channel as idle and return 0
    if(song->currentNoteRight >= song->noteCountRight)
    {
      channel->isActiveRight = 0;
      song->currentNoteRight = 0;
      song->noteTimerRight = 0;
      return 0;
    }
    //there are more notes, play the current one
    else
    {
      Note tempNote = song->notesRight[song->currentNoteRight];
      return playSine(tempNote.note, tempNote.octave, tempNote.volume, channel, 1);  
    }
  }
  else
  {
    song->noteTimerLeft++;
    //if note is finished, progress to next note
    if(song->noteTimerLeft >= song->notesLeft[song->currentNoteLeft].length)
    {
      song->currentNoteLeft++;
      song->noteTimerLeft = 0;
    }
    //if no more notes, set the channel as idle and return 0
    if(song->currentNoteLeft >= song->noteCountLeft)
    {
      channel->isActiveLeft = 0;
      song->currentNoteLeft = 0;
      song->noteTimerLeft = 0;
      return 0;
    }
    //there are more notes, play the current one
    else
    {
      Note tempNote = song->notesLeft[song->currentNoteLeft];
      return playSine(tempNote.note, tempNote.octave, tempNote.volume, channel, 1);  
    }
  }
    
}

/* interrupt routine for ABDAC */
__int_handler *dac_int(void) {
  //0xffff is the highest amplitude
  short left;
  short right;  
  
  right = play(1);
  left = play(0);

  dac->SDR.channel0 = right;
  dac->SDR.channel1 = left;
  //enable new interrupt
  dac->isr;
  return 0;
}

/* interrupt routine for buttons */  
__int_handler *button_int(void) {
  int i;
  /* debouncing */
  for(i=0; i<0xfff; i++){}

  //read button status and mask pins of interest
  i = piob->pdsr;
  i = ~i;
  i = i & 0xe7;
  //part for shifting the light (buttons 0 and 3)
  if(i==1 || i==4){
  if(i==1){
    if(light>1){
      light = light >> 1;
    }
  }
  if(i==4){
    if(light<0x80){
      light = light << 1;
    }
  }
  pioc->codr = ~0x0;
  pioc->sodr = light;
  }else{
    //controling the PM and activate DAC
    if((i==0x80)||(i==0x40)||(i == 0x20)){
      if(i==0x80){
        loadSong(musicCh_ptr, deathSong_ptr);
      }
      if(i==0x40){
        loadSong(sfxCh1_ptr, wallHitSong_ptr);     
      }
      if(i==0x20)
      {
        loadSong(sfxCh1_ptr, paddleHitSong_ptr);
      }
      sm->pm_gcctrl[6] = 0x0005; 
      dac->CR.en = 1;
      dac->IER.tx_ready = 1;
    }
    if(i==0x02){//stop DAC with button 1
      dac->CR.en = 0;
    }  
  }
  //allow new interrupts
  i = piob->isr;
  return 0;
}
